package uk.co.mmscomputing.device.twain;

import java.io.File;

public class TwainTransfer implements TwainConstants, TwainITransfer {

	TwainSource	source;
	boolean		isCancelled;

	public TwainTransfer(TwainSource source) {
		this.source = source;
		isCancelled = false;
	}

	public void initiate() throws TwainIOException {
		commitCancel();
	}

	public void setCancel(boolean isCancelled) { // program cancelled next scan
		this.isCancelled = isCancelled;
	}

	protected void commitCancel() throws TwainIOException { // use before transfer setup
		if (isCancelled && (source.getState() == STATE_TRANSFERREADY)) { // state 6
			throw new TwainUserCancelException(); // state 6 -> 5
		}
	}

	public void finish() throws TwainIOException {
	} // state 7 -> 6 called when image available

	public void cancel() throws TwainIOException {
	} // state 7 -> 5 called when source (gui) cancelled

	public void cleanup() throws TwainIOException {
	} // called once at end of acquisition loop

	public static class NativeTransfer extends TwainTransfer {

		private byte[]	imageHandle	= new byte[jtwain.getPtrSize()];	// 32 bit ptr size: 4; 64 bit ptr size: 8

		public NativeTransfer(TwainSource source) {
			super(source);
		}

		public void initiate() throws TwainIOException {
			super.initiate();
			source.call(DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, imageHandle);
		}

		public void finish() throws TwainIOException {
			long handle = jtwain.getPtr(imageHandle, 0); // System.err.println("Handle = 0x"+Long.toHexString(handle));
			jtwain.transferNativeImage(handle); // convert DIB into BufferedImage and tell listeners about image
		}
	}

	public static class FileTransfer extends TwainTransfer {

		protected File	file;

		public FileTransfer(TwainSource source) {
			super(source);
			file = null;
		}

		protected int getImageFileFormat() {
			return source.getImageFileFormat();
		}

		public void setFile(File f) {
			file = f;
		}

		public File getFile() {
			if (file == null) { // make sure we have a valid file name
				String ext = ImageFileFormatExts[getImageFileFormat()];
				try {
					File dir = new File(System.getProperty("user.home"), "mmsc/tmp");
					dir.mkdirs();
					file = File.createTempFile("mmsctwain", ext, dir);
				} catch (Exception e) {
					file = new File("c:\\mmsctwain." + ext);
				}
			} // System.err.println(file.getPath());
			return file;
		}

		public void initiate() throws TwainIOException {
			super.initiate();

			String file = getFile().getPath();
			int iff = getImageFileFormat();

			byte[] setup = new byte[260]; // TW_SETUPFILEXFER
			jtwain.setString(setup, 0, file); // file path for new image
			jtwain.setINT16(setup, 256, iff); // setup.Format = i.e. TWFF_BMP;
			jtwain.setINT16(setup, 258, 0); // setup.VRefNum = 0; Mac only

			source.call(DG_CONTROL, DAT_SETUPFILEXFER, MSG_SET, setup); // tell source file path and image type
			source.call(DG_IMAGE, DAT_IMAGEFILEXFER, MSG_GET, null); // tranfer data
		}

		public void finish() throws TwainIOException {
			jtwain.transferFileImage(file); // tell listeners about new image file
		}

		public void cancel() throws TwainIOException {
			if ((file != null) && file.exists()) {
				file.delete(); // delete file if it exists
			}
		}

		public void cleanup() throws TwainIOException {
			setFile(null); // default behaviour is to setup new temp file every time
		}
	}

	public static class MemoryTransfer extends TwainTransfer {

		private byte[]	imx				= new byte[(jtwain.getPtrSize() == 4) ? 38 : 42];	// TW_IMAGEMEMXFER 32bit: 38; 64bit: 42
		private Info	info;

		protected int	minBufSize		= -1;												// default -1 don't care: accept sources preferred buffer size
		protected int	maxBufSize		= -1;
		protected int	preferredSize	= -1;

		public MemoryTransfer(TwainSource source) {
			super(source);
		}

		protected void retrieveBufferSizes() throws TwainIOException {
			byte[] setup = new byte[12]; // TW_SETUPMEMXFER
			jtwain.setINT32(setup, 0, minBufSize);
			jtwain.setINT32(setup, 4, maxBufSize);
			jtwain.setINT32(setup, 8, preferredSize);
			source.call(DG_CONTROL, DAT_SETUPMEMXFER, MSG_GET, setup); // get preferred buffer size
			minBufSize = jtwain.getINT32(setup, 0);
			maxBufSize = jtwain.getINT32(setup, 4);
			preferredSize = jtwain.getINT32(setup, 8);
		}

		public void initiate() throws TwainIOException {
			super.initiate();
			retrieveBufferSizes(); //
			jtwain.nnew(imx, preferredSize); // allocate native memory buffer
			byte[] buf = new byte[preferredSize];
			info = new Info(imx, buf);
			while (true) { // while(call does not throw TwainIOException){
				source.call(DG_IMAGE, DAT_IMAGEMEMXFER, MSG_GET, imx); // tranfer data
				int bytesWritten = jtwain.getINT32(imx, 22);
				int bytesCopied = jtwain.ncopy(buf, imx, bytesWritten); // copy from native buffer to java buffer
				if (bytesCopied == bytesWritten) {
					jtwain.transferMemoryBuffer(info); // and tell listeners about new image buffer
				}
			}
		}

		public void finish() throws TwainIOException {
			int bytesWritten = jtwain.getINT32(imx, 22);
			int bytesCopied = jtwain.ncopy(info.getBuffer(), imx, bytesWritten); // copy from native buffer to java buffer
			if (bytesCopied == bytesWritten) {
				jtwain.transferMemoryBuffer(info); // and tell listeners about new image data buffer
			}
		}

		public void cleanup() throws TwainIOException {
			jtwain.ndelete(imx);
		}

		public static class Info {

			private byte[]	imx;
			private byte[]	buf;
			private int		len;

			Info(byte[] imx, byte[] buf) {
				this.imx = imx;
				this.buf = buf;
			}

			public byte[] getBuffer() {
				return buf;
			}

			public int getCompression() {
				return jtwain.getINT16(imx, 0);
			}

			public int getBytesPerRow() {
				return jtwain.getINT32(imx, 2);
			}

			public int getWidth() {
				return jtwain.getINT32(imx, 6);
			} // columns

			public int getHeight() {
				return jtwain.getINT32(imx, 10);
			} // rows

			public int getLeft() {
				return jtwain.getINT32(imx, 14);
			} // xoffset

			public int getTop() {
				return jtwain.getINT32(imx, 18);
			} // yoffset

			public int getLength() {
				return jtwain.getINT32(imx, 22);
			} // bytesWritten

			public int getMemFlags() {
				return jtwain.getINT32(imx, 26);
			}

			public int getMemLength() {
				return jtwain.getINT32(imx, 30);
			}

			public long getMemPtr() {
				return jtwain.getPtr(imx, 34);
			}

			public String toString() {
				String s = getClass().getName() + "\n";
				s += "\tcompression = " + getCompression() + "\n";
				s += "\tbytes per row = " + getBytesPerRow() + "\n";
				s += "\ttop = " + getTop() + " left = " + getLeft() + " width = " + getWidth() + " height = " + getHeight() + "\n";
				s += "\tbytes = " + getLength() + "\n";

				s += "\tmemory flags   = 0x" + Integer.toHexString(getMemFlags()) + "\n";
				s += "\tmemory length  = " + getMemLength() + "\n";
				s += "\tmemory pointer = 0x" + Long.toHexString(getMemPtr()) + "\n";

				return s;
			}
		}
	}
}

/*
 * System.err.println("min: "+jtwain.getINT32(setup,0));
 * System.err.println("max: "+jtwain.getINT32(setup,4));
 * System.err.println("preferred: "+preferred);
 */

/*
 * System.err.println("compression "+jtwain.getINT16(imx,0));
 * System.err.println("bytesPerRow "+jtwain.getINT32(imx,2));
 * System.err.println("columns "+jtwain.getINT32(imx,6));
 * System.err.println("rows "+jtwain.getINT32(imx,10));
 * System.err.println("xoffset "+jtwain.getINT32(imx,14));
 * System.err.println("yoffset "+jtwain.getINT32(imx,18));
 * System.err.println("bytes written "+jtwain.getINT32(imx,22));
 * 
 * System.err.println("imx flags 0x"+Integer.toHexString(jtwain.getINT32(imx,26)));
 * System.err.println("imx len "+jtwain.getINT32(imx,30));
 * System.err.println("imx theMem 0x"+Integer.toHexString(jtwain.getPTR(imx,34)));
 */
